﻿using SuperX.Common.Helper;
using SuperX.Common.Log;
using Fleck;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace SuperX.WebSocketService.Server
{
    public class FleckServer : WebSocketServer, IDisposable
    {
        public Action<string, string> BroadcastInfo { get; set; } = (m, n) => { };

        private ConcurrentDictionary<string, WsConnectionInfo> dic_Sockets = new ConcurrentDictionary<string, WsConnectionInfo>();

        private Thread HeartbeatcheckThread { get; set; }
        private bool _isRunning;

        public FleckServer(string location, bool supportDualStack = true) : base(location, supportDualStack)
        {
            RestartAfterListenError = true;
            ListenerSocket.NoDelay = true;
        }

        public void FleckStart()
        {
            Start(socket =>
            {
                socket.OnOpen = () => OnOpen(socket);
                socket.OnClose = () => OnClose(socket);
                socket.OnMessage = m => OnMessage(m, socket);
            });

            _isRunning = true;
            // 每隔一段时间检查连接是否可用或者超时
            HeartbeatcheckThread = new Thread(new ThreadStart(HeartbeatTimer)) { IsBackground = true };
            HeartbeatcheckThread.Start();
        }

        private void OnOpen(IWebSocketConnection socket)
        {
            //初始化新连接的客户端
            var connection = new WsConnectionInfo();
            connection.WebSocketConnection = socket;
            connection.heartbeat = DateTime.Now.Ticks;
            BroadcastInfo += connection.Send;
            if (dic_Sockets.TryAdd(socket.ConnectionInfo.Id.ToString(), connection))
                Logger.Trace(DateTime.Now.ToString() + $"-服务器:和客户端:{socket.ConnectionInfo.ClientIpAddress}:{socket.ConnectionInfo.ClientPort} 建立WebSock连接！");
        }

        private void OnClose(IWebSocketConnection socket)
        {
            if (dic_Sockets.TryRemove(socket.ConnectionInfo.Id.ToString(), out WsConnectionInfo wsConnInfo))
            {
                BroadcastInfo -= wsConnInfo.Send;
                wsConnInfo.Dispose();
                Logger.Trace(DateTime.Now.ToString() + $"-服务器:和客户端:{socket.ConnectionInfo.ClientIpAddress}:{socket.ConnectionInfo.ClientPort} 断开WebSock连接！");
            }
        }

        private void OnMessage(string message, IWebSocketConnection socket)
        {
            if (dic_Sockets.TryGetValue(socket.ConnectionInfo.Id.ToString(), out WsConnectionInfo wsConnInfo))
            {
                wsConnInfo.heartbeat = DateTime.Now.Ticks;
                if (message.Equals("ping"))
                    socket.Send(JsonHelper.JsonSerialize("pong"));
                else
                {
                    var msgObject = Message<HashSet<string>>.Convert(message);
                    if (msgObject == null) return;
                    switch (msgObject.Type)
                    {
                        case "subscribe":
                            wsConnInfo.SubscribeData = msgObject.Content;
                            break;

                        default:
                            break;
                    }
                }
            }
        }

        private void HeartbeatTimer()
        {
            while (_isRunning)
            {
                try
                {
                    foreach (var conn in dic_Sockets)
                    {
                        var isTimeout = false;
                        if (conn.Value.IsConnected)
                            isTimeout = DateTime.Now.Ticks - conn.Value.heartbeat > TimeSpan.FromSeconds(30).Ticks;
                        if (!conn.Value.IsConnected || !conn.Value.WebSocketConnection.IsAvailable || isTimeout)
                            conn.Value.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    Logger.ErrorException(ex);
                }
                finally
                {
                    Thread.Sleep(1000 * 60);
                }
            }
        }

        public ICollection<WsConnectionInfo> GetALLConnections()
        {
            return dic_Sockets.Values;
        }

        public new void Dispose()
        {
            try
            {
                _isRunning = false;
                HeartbeatcheckThread?.Abort();
                base.Dispose();
            }
            catch (Exception ex)
            {
                Logger.Warn($"Fleck Dispose:{ex.Message}");
            }
        }
    }
}